package frpc.manager.list;

import frpc.manager.basics.RedisBasicsBusinessManager;
import frpc.manager.lock.IRedisLockManager;
import frpc.manager.operation.GiveCacheData;
import org.springframework.data.redis.core.ListOperations;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 类描述:  Redis——List处理实现
 *
 * @author 王雷
 * @version 1.0.0
 * @date 2022/6/2 0002 下午 3:12
 */
@SuppressWarnings("unchecked")
public class RedisListManagerImpl extends RedisBasicsBusinessManager implements IRedisListManager {

    /**
     * 功能描述：Redis基础处理对象
     *
     * @param iRedisLockManager Redis——锁处理接口
     * @author : 王雷
     * @date : 2020/11/26 0026 下午 4:48
     */
    protected RedisListManagerImpl(IRedisLockManager iRedisLockManager) {
        super(iRedisLockManager);
    }

    /**
     * 功能描述：获取Hash处理对象完成自定义处理
     *
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> ListOperations<String, T> giveListOperations() {
        return (ListOperations<String, T>) giveRedisTemplate().opsForList();
    }

    /**
     * 功能描述：添加数据到指定的Key中
     *
     * @param key         需要插入的Key
     * @param value       需要插入的内容
     * @param listPutType 插入类型
     * @param time        失效时间，单位：秒
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, List<T> value, ListPutType listPutType, long time) {
        if (value != null && !value.isEmpty()) {
            switch (listPutType) {
                case Left:
                    giveListOperations().leftPushAll(key, value.toArray(new Object[]{}));
                    break;
                case Right:
                    giveListOperations().rightPushAll(key, value.toArray(new Object[]{}));
                    break;
                default:
                    return;
            }
            if (time > 0L) {
                expire(key, time);
            }
        }
    }

    /**
     * 功能描述：添加数据到指定的Key中，不限制时间
     *
     * @param key         需要插入的Key
     * @param value       需要插入的内容
     * @param listPutType 插入类型
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, List<T> value, ListPutType listPutType) {
        addValueByKey(key, value, listPutType, 0L);
    }

    /**
     * 功能描述：添加数据到指定的Key中
     *
     * @param key         需要插入的Key
     * @param value       需要插入的内容
     * @param listPutType 插入类型
     * @param time        失效时间，单位：秒
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, T value, ListPutType listPutType, long time) {
        if (value != null) {
            switch (listPutType) {
                case Left:
                    giveListOperations().leftPush(key, value);
                    break;
                case Right:
                    giveListOperations().rightPush(key, value);
                    break;
                default:
                    return;
            }
            if (time > 0L) {
                expire(key, time);
            }
        }
    }

    /**
     * 功能描述：添加数据到指定的Key中，不限制时间
     *
     * @param key         需要插入的Key
     * @param value       需要插入的内容
     * @param listPutType 插入类型
     * @author : 王雷
     * @date : 2022/6/2 0002 下午 3:12
     */
    @Override
    public <T> void addValueByKey(String key, T value, ListPutType listPutType) {
        addValueByKey(key, value, listPutType, 0L);
    }

    /**
     * 功能描述：删除数据
     *
     * @param key   需要处理的Key
     * @param count 删除的数量，在Redis中会存在多个数据一样的情况
     * @param value 需要删除的对象
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 5:13
     */
    @Override
    public <T> long removeValueByKey(String key, long count, T value) {
        final Long remove = giveListOperations().remove(key, count, value);
        return (remove == null) ? 0L : remove;
    }

    /**
     * 获取数据
     *
     * @param key 键
     * @return 值
     */
    @Override
    public <T> List<T> giveValueByKey(String key) {
        if (!hasKey(key)) {
            return null;
        }
        Long size = giveListOperations().size(key);
        size = (size == null) ? 0L : size;
        List<T> returnList = new ArrayList<>(16);

        for (int i = 0; i < size; i++) {
            returnList.add((T) giveListOperations().index(key, i));
        }
        return returnList;

    }

    /**
     * 获取数据
     *
     * @param key   键
     * @param start 开始下标
     * @param end   结束下标
     * @return 值
     */
    @Override
    public <T> List<T> giveValueByKey(String key, long start, long end) {
        return (List<T>) giveListOperations().range(key, start, end);
    }

    /**
     * 获取数据长度
     *
     * @param key 键
     * @return 长度
     */
    @Override
    public Long giveSizeByKey(String key) {
        return giveListOperations().size(key);
    }

    /**
     * 功能描述：取值类型
     * <br />
     * 获取值时会直接将数据删除
     *
     * @param key         需要提取的数据Key
     * @param listPutType 取值类型
     * @return : 查询结果
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 3:28
     */
    @Override
    public <T> T giveDataByKey(String key, ListPutType listPutType) {
        switch (listPutType) {
            case Left:
                return (T) giveListOperations().leftPop(key);
            case Right:
                return (T) giveListOperations().rightPop(key);
            default:
                return null;
        }
    }

    /**
     * 功能描述：通过阻塞的方式获取数据
     * <br />
     * 获取值时会直接将数据删除
     *
     * @param key         需要提取数据的Key
     * @param blockTime   等待的时间
     * @param listPutType 提取类型
     * @return : 结果
     * @author : 王雷
     * @date : 2020/11/28 0028 下午 3:42
     */
    @Override
    public <T> T blockDataByKey(String key, long blockTime, ListPutType listPutType) {
        blockTime = Math.max(blockTime, 0L);
        switch (listPutType) {
            case Left:
                return (T) giveListOperations().leftPop(key, blockTime, TimeUnit.SECONDS);
            case Right:
                return (T) giveListOperations().rightPop(key, blockTime, TimeUnit.SECONDS);
            default:
                return null;
        }
    }

    /**
     * 属性描述：业务处理锁前缀
     *
     * @date : 2020/11/30 0030 下午 6:20
     */
    protected static final String LIST_MANAGER_NULL_LOCK_PREFIX = "List_Manager_Null_Lock_Prefix_";

    /**
     * 获取数据，带锁处理
     *
     * @param key           键
     * @param giveCacheData 数据无效时的回调函数
     * @return 值
     */
    @Override
    public <T> List<T> giveValueByKeyToLock(String key, GiveCacheData<List<T>> giveCacheData, ListPutType listPutType) throws Exception {
        return giveValueByKeyToLock(key, 0, -1, giveCacheData, listPutType, null, null);
    }

    /**
     * 获取数据，带锁处理
     *
     * @param key           键
     * @param giveCacheData 数据无效时的回调函数
     * @param start         开始下标
     * @param end           结束下标
     * @return 值
     */
    @Override
    public <T> List<T> giveValueByKeyToLock(String key, long start, long end, GiveCacheData<List<T>> giveCacheData
            , ListPutType listPutType) throws Exception {
        return giveValueByKeyToLock(key, start, end, giveCacheData, listPutType, null, null);
    }

    /**
     * 获取数据，带锁处理
     *
     * @param key           键
     * @param giveCacheData 数据无效时的回调函数
     * @param putMaxTime    存储的时间
     * @return 值
     */
    @Override
    public <T> List<T> giveValueByKeyToLock(String key, long start, long end, GiveCacheData<List<T>> giveCacheData, ListPutType listPutType
            , Long putMaxTime, TimeUnit timeUnit) throws Exception {
        final ListPutType inlistPutType = (listPutType == null) ? ListPutType.Left : listPutType;
        /*
         * 第一步：检查访问缓存对象中是否有数据
         * 访问缓存中的数据是在没有获取到值或者逻辑处理出现异常的时候才会存在；
         * 存在的时间为1分钟；
         * */
        if (Boolean.TRUE.equals(hasKey(key))) {
            return giveValueByKey(key, start, end);
        }

        return iRedisLockManager.autoLockToManager(LIST_MANAGER_NULL_LOCK_PREFIX + key, 0, redisTemplate -> {
            //再次验证值是否存在
            if (Boolean.TRUE.equals(hasKey(key))) {
                return giveValueByKey(key, start, end);
            } else {
                List<T> verifyData = giveCacheData.giveManager();
                verifyData = (verifyData == null) ? new ArrayList<>() : verifyData;
                if (verifyData.isEmpty()) {
                    return verifyData;
                }
                addValueByKey(key, verifyData, inlistPutType);
                if (putMaxTime != null && putMaxTime > 0L) {
                    expire(key, putMaxTime, (timeUnit == null) ? TimeUnit.SECONDS : timeUnit);
                }
                return giveValueByKey(key, start, end);
            }
        });
    }
}
